# -*- coding: utf-8 -*-

#    OriginDB
#    Copyright (C) 2009-2010  Jason Power <power.jg@gmail.com>
#    Copyright (C) 2010       Travis Gockel <travis@gockelhut.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from PyQt4.QtGui import *
from PyQt4 import QtCore

import yaml
import resource_rc

from compoundVisual import CompoundVisual

import sys
sys.path.append('../')
from aeDatabase import Reaction

from tools import RemoveReactionVisual

class ReactionVisual(QGraphicsItem):

	# Image to put inbetween reactants and products
	arrow = QPixmap(":/images/common/draw-arrow-forward22x22.png")
	arrowSize = 22

	# Padding above and below compounds in the list
	compoundPadding = 0

	# Extra space around everything
	padding = 10
	margin = 3

	# Padding on left anr right for boxes around compounds (must be less than padding)
	compoundMargin = 5

	#x value to start the list of the compounds from the center
	compoundStartX = 20

	# font for the reference info
	font = QFont()
	_fontMetrics = QFontMetrics(font)
	fontHeight = _fontMetrics.height()

	# Padding for above the reference info
	referencePadding = 5

	# Colors for background of compounds
	c1 = QColor(255, 255, 255, 100)
	c2 = QColor(200, 200, 200, 100)
	defaultColors = [c1,c2]

	selectedColor = QColor(180,180,255)

	def __init__(self, reaction, readOnly = False):
		QGraphicsItem.__init__(self)

		self.reaction = reaction
		self.reactants = {}
		self.products = {}

		# Positions of the edges of compounds in item coords
		# Dictionary of database Compound : (position in list, position coords)
		#self.productPositions = {}
		#self.reactantPositions = {}
		#self.productColors = []
		#self.reactantColors = []

		self.currentColors = []
		
		self.reactantMaxWidth = 0
		self.productMaxWidth = 0

		self.maxCompounds = 0

		self.width = 0
		self.height = 0

		self.selected = False
		
		self.updateCompounds()

		self.rect = QtCore.QRectF()

		try:
			self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
		except AttributeError:
			pass #Need this in Qt4.6 not in Qt4.5

		self.calcRect()

		self.reference = self.reaction.reference.author_name[0:self.reaction.reference.author_name.find(';')] + " (" + str(self.reaction.reference.year) + ")"

		self.setFlag(QGraphicsItem.ItemIsMovable, True)
		self.setFlag(QGraphicsItem.ItemIsSelectable, True)

		self.connections = []

		self.setReadOnly(readOnly)

		self.setZValue(1)

	def setSelected(self, selected):
		self.selected = selected
		if selected:
			scene = self.scene()
			try:
				scene.reactionDetails.setReaction(self.reaction)
			except AttributeError:
				# If this happens then we were selected in the results frame, should ignore
				pass
		self.prepareGeometryChange()

	def setReadOnly(self, readOnly):
		self.readOnly = readOnly

		if not readOnly:
			self.setFlag(QGraphicsItem.ItemIsMovable, True)
		else:
			self.setFlag(QGraphicsItem.ItemIsMovable, False)

	def getReactants(self):
		return self.reactants.keys()

	def getProducts(self):
		return self.products.keys()

	def getProduct(self, compound):
		print self.products
		for cVis in self.products.keys():
			print cVis.compound.key
			if cVis.compound == compound:
				return cVis
				
		return None

	def getReactant(self, compound):
		print self.reactants
		for cVis in self.reactants.keys():
			print cVis.compound.key
			if cVis.compound == compound:
				return cVis

		return None

	def getProductPosition(self, cVis):
		""" Returns edge of given product in scene coords
			if it is not found then it returns None
		"""
		try:
			return self.sceneTransform().map(self.products[cVis]["pos"][1])
		except KeyError:
			print "failed!!"
			return None

	def getReactantPosition(self, cVis):
		""" Returns edge of given reactant in scene coords
			if it is not found then it returns None
		"""
		try:
			return self.sceneTransform().map(self.reactants[cVis]["pos"][1])
		except KeyError:
			return None

	def getColor(self, cVis):
		if cVis in self.products.keys():
			if self.products[cVis]["color"] == self.defaultColors[self.products[cVis]["pos"][0]%2]:
				return None
			else:
				return self.products[cVis]["color"]
		else:
			if self.reactants[cVis]["color"] == self.defaultColors[self.reactants[cVis]["pos"][0]%2]:
				return None
			else:
				return self.reactants[cVis]["color"]

	def testColor(self, color):
		""" Tests to see if we already use the color
		"""
		return not color in self.currentColors

	def setCompoundColor(self, cVis, color):
		if color:
			self.currentColors.append(color)
		else:
			if cVis in self.products.keys():
				c = self.products[cVis]["color"]
			else:
				c = self.reactants[cVis]["color"]
			for i in range(len(self.currentColors)):
				if self.currentColors[i] == c:
					del self.currentColors[i]
					break
		
		if cVis in self.products.keys():
			if not color:
				self.products[cVis]["numConn"] -= 1
				if self.products[cVis]["numConn"] == 0:
					self.products[cVis]["color"] = self.defaultColors[self.products[cVis]["pos"][0]%2]
			else:
				self.products[cVis]["numConn"] += 1
				self.products[cVis]["color"] = color
		else:
			if not color:
				self.reactants[cVis]["numConn"] -= 1
				if self.reactants[cVis]["numConn"] == 0:
					self.reactants[cVis]["color"] = self.defaultColors[self.reactants[cVis]["pos"][0]%2]
			else:
				self.reactants[cVis]["numConn"] += 1
				self.reactants[cVis]["color"] = color
		
		self.update()

	def addConnectionToUpdate(self, connection):
		self.connections.append(connection)

	def removeConnectionToUpdate(self, connection):
		for i in range(len(self.connections)):
			if self.connections[i] == connection:
				del self.connections[i]
				break

	def contextMenuEvent(self, event):
		if self.readOnly:
			return
		
		menu = QMenu()
		remove = menu.addAction("Remove Reaction")
		action = menu.exec_(event.screenPos())
		if action == remove:
			command = RemoveReactionVisual(self, self.scene())
			self.scene().undoStack.push(command)

	def updateCompounds(self):
		self.reactants = {}
		self.products = {}
		
		for reactant in self.reaction.reactants:
			cVis = CompoundVisual(reactant, parent=self)
			if cVis.boundingRect().width() > self.reactantMaxWidth:
				self.reactantMaxWidth = cVis.boundingRect().width()
			self.reactants[cVis] = {"numConn":0}

		for product in self.reaction.products:
			cVis = CompoundVisual(product, parent=self)
			if cVis.boundingRect().width() > self.productMaxWidth:
				self.productMaxWidth = cVis.boundingRect().width()
			self.products[cVis] = {"numConn":0}

		self.calcRect()

		i = 0
		y = -self.height/2.0 + self.padding
		x = -self.width/2.0 + self.padding
		for cVis in self.reactants:
			pos = QtCore.QPointF(x+cVis.boundingRect().width()/2.0, y+CompoundVisual.height/2.0)
			cVis.setPos(pos)
			self.reactants[cVis]["pos"] = (i, QtCore.QPointF(-self.width/2.0, pos.y()))
			self.reactants[cVis]["color"] = self.defaultColors[i%2]
			y += CompoundVisual.height + self.compoundPadding
			i += 1

		i = 0
		y = -self.height/2.0 + self.padding
		x = self.width/2.0 - self.padding - self.productMaxWidth
		for cVis in self.products:
			pos = QtCore.QPointF(x+cVis.boundingRect().width()/2.0, y+CompoundVisual.height/2.0)
			cVis.setPos(pos)
			self.products[cVis]["pos"] = (i, QtCore.QPointF(self.width/2.0, pos.y()))
			self.products[cVis]["color"] = self.defaultColors[i%2]
			y += CompoundVisual.height + self.compoundPadding
			i += 1


	def mousePressEvent(self, event):
		self.setZValue(11)
		QGraphicsItem.mousePressEvent(self, event)

	def mouseReleaseEvent(self, event):
		self.setZValue(1)
		QGraphicsItem.mouseReleaseEvent(self, event)


	def calcRect(self):
		self.width = self.reactantMaxWidth + self.productMaxWidth + 2.0 * self.compoundStartX + self.padding * 2
		self.maxCompounds = max(len(self.products), len(self.reactants))
		self.height = self.maxCompounds * CompoundVisual.height + (self.maxCompounds - 1) * self.compoundPadding + self.padding * 2
		self.height += self.fontHeight + self.referencePadding
		
		self.rect = QtCore.QRectF((-self.width-self.margin)/2.0,
								  (-self.height-self.margin)/2.0,
								  self.width+self.margin,
								  self.height+self.margin)
		self.prepareGeometryChange()

	def boundingRect(self):
		if self.selected:
			return self.rect.adjusted(-3,-3,3,3)
		else:
			return self.rect

	def itemChange(self, change, value):
		if change == QGraphicsItem.ItemPositionHasChanged:
			for conn in self.connections:
				conn.updateReactions()
		return QGraphicsItem.itemChange(self, change, value)

	def paint(self, painter, option, widget):
		painter.fillRect(self.rect, QColor(255,255,255))

		if self.selected:
			oldPen = painter.pen()
			pen = QPen(self.selectedColor)
			pen.setJoinStyle(QtCore.Qt.RoundJoin)
			pen.setWidth(6)
			painter.setPen(pen)
			painter.drawRect(self.rect.adjusted(3,3,-3,-3))
			painter.setPen(oldPen)
			painter.drawRect(self.rect.adjusted(3,3,-3,-3))
		else:
			painter.drawRect(self.rect)

		# Draw arrow in the middle
		painter.drawPixmap(-self.width/2.0+self.padding+self.reactantMaxWidth+self.compoundStartX-self.arrowSize/2.0,
						   -self.height/2.0+self.padding+self.maxCompounds/2.0*CompoundVisual.height+
								int(self.maxCompounds/2.0)*self.compoundPadding-self.arrowSize/2.0,
						   self.arrow)

		# Draw colors of things that aren't colored at all
		for i in range(self.maxCompounds):
			rRect = QtCore.QRectF(-self.width/2.0+self.padding-self.compoundMargin,
								  -self.height/2.0 + self.padding + (CompoundVisual.height + self.compoundPadding) * i,
								  self.reactantMaxWidth+self.compoundMargin * 2,
								  CompoundVisual.height)
			pRect = QtCore.QRectF(self.width/2.0 - self.padding - self.productMaxWidth - self.compoundMargin,
								  -self.height/2.0 + self.padding + (CompoundVisual.height + self.compoundPadding) * i,
								  self.productMaxWidth+self.compoundMargin * 2,
								  CompoundVisual.height)

			color = None
			for cVis in self.products:
				if self.products[cVis]["pos"][0] == i:
					color = self.products[cVis]["color"]
					break
			if color:
				painter.fillRect(pRect, color)
			else:
				painter.fillRect(pRect, self.defaultColors[i%2])
			
			color = None
			for cVis in self.reactants:
				if self.reactants[cVis]["pos"][0] == i:
					color = self.reactants[cVis]["color"]
					break
			if color:
				painter.fillRect(rRect, color)
			else:
				painter.fillRect(rRect, self.defaultColors[i%2])

		# Draw boxes around the compounds
		painter.drawRect(-self.width/2.0 + self.padding - self.compoundMargin,
						 -self.height/2.0 + self.padding,
						 self.reactantMaxWidth + self.compoundMargin * 2,
						 self.maxCompounds * (CompoundVisual.height + self.compoundPadding))
		painter.drawRect(self.width/2.0 - self.padding - self.compoundMargin - self.productMaxWidth,
						 -self.height/2.0 + self.padding,
						 self.productMaxWidth + self.compoundMargin * 2,
						 self.maxCompounds * (CompoundVisual.height + self.compoundPadding))

		# Draw the reference info at the bottom
		w = self._fontMetrics.width(self.reference)
		painter.drawText(-w/2.0, self.height/2.0 - self.padding, self.reference)
